#ifndef NIGHT_TENSOR_CORE
#define NIGHT_TENSOR_CORE

#include "../night.hpp"

namespace night {

namespace tensor {

template< size_type Size, typename Index >
struct dimension {
	typedef Index type;
	static size_type const size = Size;

	size_type value;

	explicit dimension ( size_type src = 0 ) : value (src) {}

	operator size_type () const { return this->value; }

	dimension & operator ++ () { ++this->value; return *this; }
	dimension & operator -- () { --this->value; return *this; }

	dimension operator ++ ( int ) { dimension tmp (this->value); ++this->value; return tmp; }
	dimension operator -- ( int ) { dimension tmp (this->value); --this->value; return tmp; }
};

template< typename T=void, typename Tail=void > struct shape;
template< typename Shape > struct for_each;

template< typename E, typename R = typename E::result_type, typename S = typename E::shape_type > 
struct Base;

template< typename E, typename R, typename S > 
struct Expr;

template< typename E, typename R, typename S > 
struct reference_tensor;
template< template<typename> class Op, typename LE, typename LR, typename LS, typename RE, typename RR, typename RS >
struct composite_tensor;
template< template<typename> class Op, typename RE, typename RR, typename RS > 
struct unary_tensor;
template< template<typename> class Op, typename LE, typename LR, typename LS, typename RE, typename RR, typename RS > 
struct binary_tensor;

template< typename E > 
struct scalar_view;
template< typename E, typename R, typename V = E, typename S = shape<>, int n = 0 >
struct bind_view;
template< typename E, typename R, typename I, typename V = E, typename S = shape<>, int n = 0 >
struct tensor_view;


template< typename E, typename R, typename S > 
struct Base : detail::Base< Base<E,R,S> >
{
	typedef typename detail::remove_cref<R>::type value_type;
	typedef R result_type;
	typedef S shape_type;
	typedef Base base_type;

	template< typename LE, typename RE >
	struct Assign {
		typename LE::value_type lhs;
		typename RE::value_type rhs;

		Assign ( typename LE::param_type x, typename RE::param_type y ) : lhs (x), rhs (y) {}

		template< typename Shape >
		void operator () ( Shape const & s ) const { this->lhs (s) = this->rhs (s); }
	};

	template< typename Shape >
	result_type operator () ( Shape const & s ) const { return derived<E> (this)->at (s); }

	template< typename RE, typename RR, typename RS >
	E & assign ( Base<RE,RR,RS> const & rhs ) { 
		for_each<shape_type>::apply ( 
			typename detail::instance_first<shape<>,E>::type (), 
			Assign< by_ref<Base>, by_cref< Base<RE,RR,RS> > > (*this,rhs) );
		return derived<E> (*this); 
	}

	E & assign ( value_type const & rhs ) { 
		typedef scalar_view< by_cref<value_type> > scalar_tensor;
		for_each<shape_type>::apply ( 
			typename detail::instance_first<shape<>,E>::type (), 
			Assign< by_ref<Base>, by_val<scalar_tensor> > (*this,scalar_tensor(rhs)) );
		return derived<E> (*this); 

	}
};

template<> struct for_each< shape<> >
{
	template< typename Shape, typename Op >
	static void apply ( Shape const & s, Op const & op ) { op ( s ); }
};

template< typename Dimension, typename Tail >
struct for_each< shape<Dimension,Tail> >
{
	template< typename Shape, typename Op >
	static void apply ( Shape const & s, Op const & op ) {
		for ( Dimension k = Dimension (0); k< Dimension::size; ++k ) {
			for_each<Tail>::apply ( s << k, op );
		}
	}
};

} // end namespace night::tensor

} // end namespace nigth

#endif
